package cz.semjobKB.search.config.impl;

import cz.semjobKB.conllx.node.EConstituent;
import cz.semjobKB.conllx.node.EWordType;
import cz.semjobKB.search.api.IRulesSet;
import cz.semjobKB.search.api.RuleCorrectnessException;
import cz.semjobKB.search.config.api.ISearchConfiguration;
import cz.semjobKB.search.impl.NodeRule;
import cz.semjobKB.search.impl.Rule;
import cz.semjobKB.search.impl.RulesSet;
import cz.semjobKB.search.impl.SearchRules;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;

@Component
public class SearchConfiguration implements ISearchConfiguration {

	private Logger logger = Logger.getLogger(SearchConfiguration.class);
	
	public SearchRules loadXMLConfiguration	(String path) {
		SearchRules rulesConfiguration = new SearchRules();
		try {
			File fXmlFile = new File(path);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder	dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);

			NodeList ruleSetList = doc.getElementsByTagName("ruleset");

			//Gets trough each rule set and extract it
			for (int position = 0; position < ruleSetList.getLength(); position++) {

				Node ruleSetNode = ruleSetList.item(position);				

				Element eEl = (Element) ruleSetNode;

				switch (eEl.getAttribute("type")) {
				case "predicate" : rulesConfiguration.setPredicateRule(parseRuleSet(ruleSetNode, "PREDICATE", true)); break;
				case "subject" : rulesConfiguration.setSubjectRule(parseRuleSet(ruleSetNode, "SUBJECT", false)); break;
				case "object" : rulesConfiguration.setObjectRule(parseRuleSet(ruleSetNode, "OBJECT", false)); break;
				case "terminology" : rulesConfiguration.setTerminologyRule(parseRuleSet(ruleSetNode, "TERMINOLOGY", false)); break;
				default: throw new RuleCorrectnessException("Rule group type is not presented or set!");
				}
			}

		} catch (ParserConfigurationException e) {			
			logger.error(e.getMessage());
		} catch (SAXException e) {
			logger.error(e.getMessage());
		} catch (IOException ex) {
			logger.error(ex.getMessage());
		} catch (RuleCorrectnessException exx) {
			logger.error(exx.getMessage());
		} catch (IllegalArgumentException exIl) {
			logger.error("XML config file contains invalid field!!");
		}
					
		return rulesConfiguration;		
	}
    /*
     * Extract one rule set from the xml document
     */
	private IRulesSet parseRuleSet(Node ruleSetNode, String ruleName, boolean isPredicate) throws RuleCorrectnessException {
		RulesSet ruleSet = new RulesSet(null);
		NodeList rules = ruleSetNode.getChildNodes();
		boolean isFirst = true;
		
		for (int positionRule = 0; positionRule < rules.getLength(); positionRule++) {

			Node ruleNode = rules.item(positionRule);
			if (!ruleNode.getNodeName().toString().equals("rule")) {
				continue;
			}
			 
			NodeList nodes = ruleNode.getChildNodes();
			Rule rule;
			if (isFirst && isPredicate) {
				rule = new Rule(ruleName, true);
				isFirst = false;
			} else {
				rule = new Rule(ruleName, false);
			}
			
			for (int possitionNode = 0; possitionNode < nodes.getLength(); possitionNode++) {
				Node node = nodes.item(possitionNode);
				
				if (!node.getNodeName().toString().equals("node")) {
					continue;
				}

				NodeList nodeElements = node.getChildNodes();

				EConstituent constituent = null;
				boolean isVassal = false;
				EConstituent parentConstituent = null;
				EWordType wordType = null;
				for (int possitionElement = 0; possitionElement < nodeElements.getLength(); possitionElement++) {					
					
					switch (nodeElements.item(possitionElement).getNodeName().toString()) {
						case "constituent" : constituent = EConstituent.valueOf(nodeElements.item(possitionElement).getTextContent()); break;
						case "vassal" : isVassal = new Boolean(nodeElements.item(possitionElement).getTextContent()); break;
						case "parentType" : if (!nodeElements.item(possitionElement).getTextContent().toString().equals(("null"))) {
												parentConstituent = EConstituent.valueOf(nodeElements.item(possitionElement).getTextContent());
											} 
											break;
						case "wordType" : 	if (!nodeElements.item(possitionElement).getTextContent().toString().equals(("null"))) {
												wordType = EWordType.valueOf(nodeElements.item(possitionElement).getTextContent()); 
											}
											break;						

					}
				}
				NodeRule nodeRule = new NodeRule(constituent, isVassal, parentConstituent, wordType);
				rule.addNode(nodeRule);

			}
			ruleSet.addRule(rule);
		}

		return ruleSet;
	}
}
